from typing import Optional

from PySide6.QtCore import QRectF, QPointF
from PySide6.QtGui import QPen, QColor, QBrush, QFont, QPainter, QPainterPath, QPolygonF, Qt, QFontMetrics
from PySide6.QtWidgets import QGraphicsItem, QStyleOptionGraphicsItem, QWidget

PORT_TYPE_IN = 0  # 输入插槽
PORT_TYPE_OUT = 1  # 输出插槽


class NodePort(QGraphicsItem):
    def __init__(self, parent=None):
        super().__init__(parent)

        self._connected_ports = []  # 用于存储连接的插槽
        self._edges = []  # 用于存储连接的边

        self.scene = None  # 用于存储场景
        self.parent_node = None  # 父节点
        self._port_label = '通用'  # 插槽的标签
        self._port_type = PORT_TYPE_IN
        self._port_color = QColor('#ffffff')  # 插槽的颜色

        # 定义Pen和Brush
        self._pen_default = QPen(self._port_color)
        self._pen_default.setWidthF(1.5)  # 设置线宽
        self._brush_default = QBrush(self._port_color)
        self._font_size = 10  # 插槽标签的字体大小
        self._port_font = QFont('consolas', self._font_size)  # 设置字体

        self._port_icon_size = 20  # 插槽的大小
        self._port_label_size = len(self._port_label) * self._font_size  # 插槽标签的大小
        self._port_total_width = self._port_icon_size + self._port_label_size  # 插槽的总宽度

    def boundingRect(self) -> QRectF:  # 返回插槽的矩形区域，用于碰撞检测等
        return QRectF(0, 0, self._port_total_width, self._port_icon_size)

    def add_to_parent_node(self, parent_node, scene):
        """将插槽添加到节点上"""
        self.setParentItem(parent_node)
        self.parent_node = parent_node
        self.scene = scene

    def add_edge(self, edge, port):
        """添加边
        :param edge: 边
        :param port: 连接的插槽
        """
        self.parent_node.add_connected_node(port.parent_node, edge)
        self._edges.append(edge)
        self._connected_ports.append(port)

    def get_port_pos(self) -> QPointF:
        """获取插槽的中心位置"""
        return self.scenePos() + QPointF(0.5 * self._port_total_width, 0.5 * self._port_icon_size)

    @property
    def is_connected(self):
        return len(self._edges) > 0  # 判断节点是否连接

    def get_port_color(self):  # 获取插槽的颜色,用于边的颜色
        return self._port_color

    @property
    def port_type(self):
        return self._port_type


class IN_Port(NodePort):
    """输入插槽"""

    def __init__(self, parent=None):
        super().__init__(parent)
        self._port_label = 'Input'  # 插槽的标签
        self._port_type = PORT_TYPE_IN

        # 颜色和线宽
        self._port_color = QColor('#99ff22')
        self._pen_default = QPen(self._port_color)
        self._pen_default.setWidthF(1)

        # 计算标签的实际宽度
        font_metrics = QFontMetrics(self._port_font)
        self._port_label_size = font_metrics.horizontalAdvance(self._port_label)
        self._port_total_width = self._port_icon_size + self._port_label_size

    def paint(self, painter: QPainter, option: 'QStyleOptionGraphicsItem', widget: Optional[QWidget] = None) -> None:
        # 绘制port的边框
        port_outline = QPainterPath()
        poly = QPolygonF()
        poly.append(QPointF(0, 0.2 * self._port_icon_size))
        poly.append(QPointF(0.4 * self._port_icon_size, 0.2 * self._port_icon_size))
        poly.append(QPointF(0.6 * self._port_icon_size, 0.5 * self._port_icon_size))
        poly.append(QPointF(0.4 * self._port_icon_size, 0.8 * self._port_icon_size))
        poly.append(QPointF(0, 0.8 * self._port_icon_size))
        port_outline.addPolygon(poly)

        if not self.is_connected:
            painter.setPen(self._pen_default)
            painter.setBrush(Qt.BrushStyle.NoBrush)
            painter.drawPath(port_outline.simplified())
        else:
            painter.setPen(Qt.PenStyle.NoPen)
            painter.setBrush(QBrush(self._port_color))
            painter.drawPath(port_outline.simplified())

        # 显示port的标签
        painter.setPen(self._pen_default)
        painter.setFont(self._port_font)  # 设置字体
        # 计算文本绘制矩形的起始位置和大小
        text_rect = QRectF(self._port_icon_size, 0, self._port_label_size, self._port_icon_size)
        painter.drawText(
            text_rect,
            Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter,
            self._port_label
        )

    def get_port_pos(self) -> QPointF:
        """获取插槽的中心位置"""
        port_pos = self.scenePos()
        # 获取插槽的中心位置
        return QPointF(
            port_pos.x() + 0.3 * self._port_icon_size,
            port_pos.y() + 0.5 * self._port_icon_size
        )


class OUT_Port(NodePort):
    """输出插槽"""

    def __init__(self, parent=None):
        super().__init__(parent)
        self._port_label = 'Normal'  # 插槽的标签
        self._port_type = PORT_TYPE_OUT

        # 颜色和线宽
        self._port_color = QColor('#87CEFA')  # Medium dark blue color
        self._pen_default = QPen(self._port_color)
        self._pen_default.setWidthF(1)

        # 计算标签的实际宽度
        font_metrics = QFontMetrics(self._port_font)
        self._port_label_size = font_metrics.horizontalAdvance(self._port_label)
        self._port_total_width = self._port_icon_size + self._port_label_size

        # 图像和文字之间的间隔
        self._spacing = 10

    def paint(self, painter: QPainter, option: 'QStyleOptionGraphicsItem', widget: Optional[QWidget] = None) -> None:
        # 显示port的标签
        painter.setPen(self._pen_default)
        painter.setFont(self._port_font)  # 设置字体
        # 计算文本绘制矩形的起始位置和大小
        text_rect = QRectF(0, 0, self._port_label_size, self._port_icon_size)
        painter.drawText(
            text_rect,
            Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter,
            self._port_label
        )

        # 绘制port的边框
        port_outline = QPainterPath()
        poly = QPolygonF()
        icon_offset_x = self._port_label_size + self._spacing  # 图形 X 位置偏移量，增加间隔
        poly.append(QPointF(icon_offset_x, 0.2 * self._port_icon_size))
        poly.append(QPointF(icon_offset_x + 0.4 * self._port_icon_size, 0.2 * self._port_icon_size))
        poly.append(QPointF(icon_offset_x + 0.6 * self._port_icon_size, 0.5 * self._port_icon_size))
        poly.append(QPointF(icon_offset_x + 0.4 * self._port_icon_size, 0.8 * self._port_icon_size))
        poly.append(QPointF(icon_offset_x, 0.8 * self._port_icon_size))
        port_outline.addPolygon(poly)

        if not self.is_connected:
            painter.setPen(self._pen_default)
            painter.setBrush(Qt.BrushStyle.NoBrush)
            painter.drawPath(port_outline.simplified())
        else:
            painter.setPen(Qt.PenStyle.NoPen)
            painter.setBrush(QBrush(self._port_color))
            painter.drawPath(port_outline.simplified())

    def get_port_pos(self) -> QPointF:
        """获取插槽的中心位置"""
        port_pos = self.scenePos()
        # 获取插槽的绘制的图像部分的中心位置
        return QPointF(
            port_pos.x() + self._port_label_size + self._spacing + 0.3 * self._port_icon_size,
            port_pos.y() + 0.5 * self._port_icon_size
        )


class ERROR_Port(OUT_Port):
    """错误输出插槽"""

    def __init__(self, parent=None):
        super().__init__(parent)
        self._port_label = 'Error'  # 插槽的标签

        # 颜色和线宽
        self._port_color = QColor('#FF7256')  # Red color
        self._pen_default = QPen(self._port_color)
        self._pen_default.setWidthF(1)

        # 图像和文字之间的间隔
        self._spacing = 10

    def get_port_pos(self) -> QPointF:
        """获取插槽的中心位置"""
        port_pos = self.scenePos()
        # 获取插槽的绘制的图像部分的中心位置
        return QPointF(
            port_pos.x() + self._port_label_size + self._spacing + 0.3 * self._port_icon_size,
            port_pos.y() + 0.5 * self._port_icon_size
        )
